package com.nage.engine;

import java.io.IOException;

import android.util.Log;

import com.nage.components.Component;
import com.nage.graphics.RenderSystem;
import com.nage.input.InputSystem;
import com.nage.multiplayer.MultiplayerSystem;

public class SimulationSystem extends Thread {

	private static final int MAX_GAME_REFRESH_RATE = 30;
	private static final long MIN_LOOP_TIME = 1000 / MAX_GAME_REFRESH_RATE;
	
	private boolean game_running;
	private long prev_update, current_update; // time in ms of the last update to game state
	private GameGraph m_gamegraph;
	private RenderSystem m_rendersystem;
	public InputSystem m_inputsystem;
	private MultiplayerSystem m_multiplayersystem;
	boolean run_simulation;
	private boolean m_graphupdated;
	private GameGraph m_gamegraphnew;
	
	public SimulationSystem(GameGraph gg) {

		game_running = true;
		run_simulation = true;
		m_gamegraph = gg;
	}
	
	/**
	 * Called once each subsystem has been set up, enabling the subsystems to
	 * link themselves up with each other.
	 * 
	 * @param ge Instance of game engine, used to grab other subsystems
	 */
	public void link(GameEngine ge) {
		m_rendersystem = ge.rs;
		m_inputsystem = ge.is;
		m_multiplayersystem = ge.ms;
	}

	public void run() {

		prev_update = System.currentTimeMillis();
		current_update = prev_update;
		Log.i("SimulationSystem", "Simulation starting. Thread born.");

		while (run_simulation) {
			if (game_running) {

				while (getTimeDelta() < MIN_LOOP_TIME) {
					try {
						//Log.d("GameSimulationSystem", "Time to spare. Sleep time = "+(MIN_LOOP_TIME - getTimeDelta()));
						sleep(MIN_LOOP_TIME - getTimeDelta());
						current_update = System.currentTimeMillis();
					} catch (InterruptedException e) {
						Log.d("GameObjectSystem",
								"Sleep unexpectedly interrupted!");
					}
				}
				current_update = System.currentTimeMillis();
				updateGameState(getTimeDelta());
				prev_update = current_update;
				
				recordSpeedData();
				
			} else {
				try {
					synchronized (this) {
						wait();
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

		}
		Log.i("SimulationSystem", "Simulation ending. Thread dying.");

	}

	private void recordSpeedData() {
		Statistics.updateTime = System.currentTimeMillis() - prev_update;
		if(Statistics.updateTime < Statistics.minUpdateTime) {
			Statistics.minUpdateTime = Statistics.updateTime;
		}
		if(Statistics.updateTime > Statistics.maxUpdateTime) {
			Statistics.maxUpdateTime = Statistics.updateTime;
		}
		Statistics.sum += Statistics.updateTime;
		Statistics.count++;
		
	}

	public void onPause() {
		game_running = false;
	}

	public void onResume() {
		game_running = true;
		synchronized (this) {
			notify();
		}
	}

	private long getTimeDelta() {
		return current_update - prev_update;
	}

	private void updateGameState(long delta) {
		
		
		// then update each object in the game graph.
		for (GameObject obj : m_gamegraph.getGraph()) {
			obj.update(delta);
		}
		
		
		// add new objects to game graphg
		if(m_gamegraph.addWaiting) {
			// add any new objects into the game graph
			for(GameObject g : m_gamegraph.addQueue ) {
				m_gamegraph.add(g);
			}
			m_gamegraph.addQueue.clear();
			m_gamegraph.addWaiting = false;
		}
		
		// remove deleted objects from game graph
		if(m_gamegraph.removeWaiting) {
			for(GameObject g : m_gamegraph.removeQueue ) {
				m_gamegraph.remove(g);
			}
			m_gamegraph.removeQueue.clear();
			m_gamegraph.removeWaiting = false;
		}
		
		if(m_graphupdated) {
		//	Log.d("SimulationSystem", "Somehow managed to update gamegraph with new one!");
		//	Log.d("SimulationSystem", "::::::::::::::::New Graph::::::::::::");
		//	m_gamegraphnew.print();
			
			// Loop through the new graph and tell the old graph to replace
			// its matching versions of the ones in the new graph.
			for(GameObject obj : m_gamegraphnew.graph) {
				for(Component comp : obj.objectComponents) {
					m_gamegraph.replace(comp);
				}
				m_gamegraph.replace(obj);
			}
			
			m_graphupdated = false;
		}

	}
	


	public boolean isMultiplayer() {
		// TODO Auto-generated method stub
		return m_gamegraph.isMultiplayer();
	}
	


	public byte[] flatten_gamegraph() throws IOException {
		try {
			return GameGraphBuilder.flatten(m_gamegraph);
		} catch (GameGraphException e) {

			e.printStackTrace();
			return null;
		}
		
	}

	public void updateGraph(GameGraph graph) {
		m_gamegraphnew = graph;
		m_graphupdated = true;
	}

	public GameGraph getGameGraph() {
		return m_gamegraph;
	}



}
